package com.tokaplot;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposePathEffect;
import android.graphics.LinearGradient;
import android.graphics.RectF;
import android.graphics.CornerPathEffect;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.graphics.Xfermode;
//import android.util.Log;

public class LineRender {
    
    //private static final String TAG = "Tokaplot";
    
	public final static PathEffect pPathDashStyle1 = new DashPathEffect(new float[]{9.0f, 9.0f}, 0);
	public final static PathEffect pPathDashStyle2 = new DashPathEffect(new float[]{12.0f, 6.0f}, 0);
	public final static PathEffect pPathDashStyle3 = new DashPathEffect(new float[]{1.0f, 8.0f}, 0);
	public final static PathEffect pPathDashStyle4 = new DashPathEffect(new float[]{1.0f, 16.0f}, 0);
	
	public final static PathEffect pPathCurveStyle1 = new CornerPathEffect(5.0f);
	public final static PathEffect pPathCurveStyle2 = new CornerPathEffect(10.0f);
	public final static PathEffect pPathCurveStyle3 = new CornerPathEffect(15.0f);
	public final static PathEffect pPathCurveStyle4 = new CornerPathEffect(20.0f);
	
	public final static Xfermode xMode_XOR = new PorterDuffXfermode(PorterDuff.Mode.XOR);
	public final static Xfermode xMode_ATOP = new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP);
	
	public static int makeStyle(int compo, int grad, int curve, int effect, int dash, int width, int type) {
		
		// type: 0 - line, 1 - filled		
		return (int)(((compo)<<24)  | ((grad&0xF)<<20) | ((curve&0xF)<<16) | ((effect&0xF)<<12) | ((dash&0xF)<<8) | ((width&0xF)<<4) | (type&0xF));
	}
	
	public static int getStyleType(int style)	{ return style&0x0F; }
	public static int getStyleWidth(int style) 	{ return (style>>4)&0x0F; }
	public static int getStyleDash(int style)	{ return (style>>8)&0x0F; }
	public static int getStyleEffect(int style)	{ return (style>>12)&0x0F; }
	public static int getStyleCurve(int style) 	{ return (style>>16)&0x0F; }
	public static int getStyleGrad(int style) 	{ return (style>>20)&0x0F; }
	public static int getStyleCompo(int style) 	{ return (style>>24)&0x0F; }
	
	public static void renderPath(Canvas c, Path p, int colour, int style, int flags) {
		
		// Get style
        int st_type 	= getStyleType(style);
        int st_width 	= getStyleWidth(style);
        int st_dash 	= getStyleDash(style);
        int st_effect 	= getStyleEffect(style);
        int st_curve 	= getStyleCurve(style);
        int st_grad 	= getStyleGrad(style);
        int st_compo	= getStyleCompo(style);
        
        // Make local copy of path
        Path path = new Path(p);
        
        // Close filled paths
        if (st_type == 1) {
        	path.close();
        }
        
        // Render paint
		Paint pr = new Paint(flags);
		pr.setColor(colour);		
		pr.setStrokeCap(Paint.Cap.ROUND);
		pr.setStrokeJoin(Paint.Join.ROUND);
		
		// Shadow paint
		Paint ps = new Paint(flags);
		ps.setColor(0xFF000000);
		ps.setAlpha(Color.alpha(colour)>>1);
		ps.setStyle(Paint.Style.STROKE);
		ps.setStrokeCap(Paint.Cap.ROUND);
		ps.setStrokeJoin(Paint.Join.ROUND);
		
		// Highlight paint
		Paint ph = new Paint(flags);
		ph.setColor(0xFFFFFFFF);
		ph.setAlpha(Color.alpha(colour)>>1);
		ph.setStyle(Paint.Style.STROKE);
		ph.setStrokeCap(Paint.Cap.ROUND);
		ph.setStrokeJoin(Paint.Join.ROUND);
		
		// Set stroke width and fill style
		float sWidth = (float)(st_width + 1);
		if (st_type == 1) {
			pr.setStyle(Paint.Style.FILL);
		} else {
			pr.setStyle(Paint.Style.STROKE);
			pr.setStrokeWidth(sWidth);
		}
		
		// Set stroke dashes
		PathEffect pEffectDash = null;
		switch (st_dash) {
			case 1:	pEffectDash = pPathDashStyle1; break;
			case 2:	pEffectDash = pPathDashStyle2; break;
			case 3:	pEffectDash = pPathDashStyle3; break;
			case 4:	pEffectDash = pPathDashStyle4; break;
		}
		
		// Set stroke curve
		PathEffect pEffectCurve = null;
		switch (st_curve) {
			case 1:	pEffectCurve = pPathCurveStyle1; break;
			case 2:	pEffectCurve = pPathCurveStyle2; break;
			case 3:	pEffectCurve = pPathCurveStyle3; break;
			case 4:	pEffectCurve = pPathCurveStyle4; break;
		}
		
		// Set gradient
		if (st_grad>0) {
			
			// Get bounds of path
			RectF b = new RectF();
			p.computeBounds(b, true);
			
			// Create gradient
			switch (st_grad) {
				case 1: pr.setShader(new LinearGradient(b.right, b.bottom, b.left, b.top, colour, ps.getColor(), Shader.TileMode.CLAMP)); break;				
				case 2: pr.setShader(new LinearGradient(b.right, b.top, b.left, b.bottom, colour, ps.getColor(), Shader.TileMode.CLAMP)); break;
				case 3: pr.setShader(new LinearGradient(b.left, b.bottom, b.right, b.top, colour, ps.getColor(), Shader.TileMode.CLAMP)); break;
				case 4: pr.setShader(new LinearGradient(b.left, b.top, b.right, b.bottom, colour, ps.getColor(), Shader.TileMode.CLAMP)); break;
			}
		}
		
		// Set effect
		PathEffect pFinalEffect = null;
		if (pEffectDash!=null && pEffectCurve!=null) {
			pFinalEffect = new ComposePathEffect(pEffectDash, pEffectCurve);
		} else if (pEffectDash != null) {
			pFinalEffect = pEffectDash;
		} else  if (pEffectCurve != null) {
			pFinalEffect = pEffectCurve;
		}
		if (pFinalEffect != null) {
			pr.setPathEffect(pFinalEffect);
			ps.setPathEffect(pFinalEffect);
			ph.setPathEffect(pFinalEffect);
		}
		
		// Set composite mode		
		if (st_compo == 1) {
			
			// XOR
			pr.setXfermode(xMode_XOR);
		} else if (st_compo == 2) {
			
			// ATOP
			pr.setXfermode(xMode_ATOP);
		}
		
		// Render path
		if (st_effect == 1) {
			
			// Shadow effect
			if (st_type == 1)
				ps.setStyle(Paint.Style.FILL);
			else
				ps.setStyle(Paint.Style.STROKE);
			
			ps.setStrokeWidth(sWidth);
			
            path.offset(sWidth*0.5f, sWidth*0.5f);
            c.drawPath(path, ps);			
			path.offset(-sWidth*0.5f, -sWidth*0.5f);
  			c.drawPath(path, pr);
			
		} else if (st_effect == 2) {
			
			// Highlight effect
			ps.setStyle(Paint.Style.STROKE);
			ph.setStyle(Paint.Style.STROKE);
			
			ps.setStrokeWidth(sWidth*0.5f);
			ph.setStrokeWidth(sWidth*0.5f);
			
			c.drawPath(path, pr);
			path.offset(0.0f, -sWidth*0.375f);
			c.drawPath(path, ph);
			path.offset(0.0f, sWidth*0.75f);
  			c.drawPath(path, ps);
  			
		} else if (st_effect == 3) {
			
			// Glow effect
			ps.setStyle(Paint.Style.STROKE);
			ph.setStyle(Paint.Style.STROKE);
			
			ph.setStrokeWidth(sWidth*4.0f);
			ph.setAlpha(0x10);
			c.drawPath(path, ph);
			ph.setStrokeWidth(sWidth*3.0f);
			ph.setAlpha(0x20);
			c.drawPath(path, ph);
			pr.setStrokeWidth(sWidth*2.0f);
			pr.setAlpha(0x40);
			c.drawPath(path, pr);
			pr.setAlpha(0x80);
			pr.setStrokeWidth(sWidth);
			c.drawPath(path, pr);
			
		}  else if (st_effect == 4) {
			
			// Bendy effect
			ps.setStrokeWidth(sWidth*2.0f);
			ps.setAlpha(0xFF);
			ps.setPathEffect(pPathCurveStyle2);
			c.drawPath(path, ps);
			pr.setStrokeWidth(sWidth);
			c.drawPath(path, pr);
			
		} else {
			
			// Normal effect
			c.drawPath(path, pr);
		}
	}
}
